using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Globalization;
using System.IO;
using System.Reflection;
using CreamX.Framework.Game;
using CreamX.Framework.Game.Components;
using CreamX.Framework.SceneGraph;


namespace CreamX.Framework.Serialization
{
    public class CreamXSceneDataDeSerializer
    {
        CreamXSceneData _currentData;
        public List<Assembly> Assemblies = new List<Assembly>();
        
        public object GetInstance(string name)
        {
            foreach (Assembly asm in Assemblies)
            {
                object o = asm.CreateInstance(name);
                if (o != null)
                    return o;
            }
            return null;
        }
        string _fileRoot = "";
        internal void DeSerialize(string scenefilename, CreamXSceneData creamXSceneData)
        {
            try
            {
                string filename = Path.GetFullPath(scenefilename);
                if (!File.Exists(filename))
                    throw new FileNotFoundException();

                _fileRoot = Path.GetDirectoryName(filename);
                _currentData = creamXSceneData;
                XmlDocument __doc = new XmlDocument();
                __doc.Load(scenefilename);

                //Load Physics
                LoadSceneSettings(__doc,creamXSceneData);
                //Load Camera
                LoadCamera(creamXSceneData, __doc);
                //Load Materials
                LoadMaterials(creamXSceneData, __doc);
                //Load TileMap
                LoadTileMap(creamXSceneData, __doc);
                LoadAnimationData(creamXSceneData, __doc);
                //Load Objects
                LoadObjects(creamXSceneData, __doc);
                foreach (CreamXObject obj in creamXSceneData.TemplateObjects)
                {
                    CreamXMountPointComponent __n = obj.GetComponent<CreamXMountPointComponent>();
                    if (__n != null)
                    {
                        foreach (Mount mnt in obj.MountedObjects)
                        {
                            mnt.Object = creamXSceneData.GetTemplate(mnt.ObjectRef);
                        }
                    }
                    
                }
                foreach (CreamXObject obj in creamXSceneData.Objects)
                {
                    CreamXMountPointComponent __n = obj.GetComponent<CreamXMountPointComponent>();
                    if (__n != null)
                    {
                        foreach (Mount mnt in obj.MountedObjects)
                        {
                            mnt.Object = creamXSceneData.GetTemplate(mnt.ObjectRef);
                        }
                    }

                }
                
            }
            catch (Exception err)
            {
                //throw new ArgumentException("Bad File");
                //Console.Write(err.ToString());
            }
        }

        private void LoadAnimationData(CreamXSceneData creamXSceneData, XmlDocument __doc)
        {
              XmlNode __el = __doc.SelectSingleNode("CreamXSceneData/AnimationData");
              if (__el != null&&__el.ChildNodes.Count > 0)
              {
                  foreach (XmlNode __node in __el.ChildNodes)
                  {
                      AnimationInfo __i = new AnimationInfo();
                      __i.Name = __node.Attributes["name"].InnerText;
                      try{
                      __i.TileCols = int.Parse(__node.SelectSingleNode("Cols").InnerText);
                      __i.TileRows = int.Parse(__node.SelectSingleNode("Rows").InnerText);
                      __i.SizeX = int.Parse(__node.SelectSingleNode("SizeX").InnerText);
                      __i.SizeY = int.Parse(__node.SelectSingleNode("SizeY").InnerText);
                      __i.FrameCount = int.Parse(__node.SelectSingleNode("FrameCount").InnerText);
                      __i.AnimationSpeed = int.Parse(__node.SelectSingleNode("AnimationSpeed").InnerText);
                      
                          __i.CurrentFrame = int.Parse(__node.SelectSingleNode("CurrentFrame").InnerText);
                          
                          XmlNode __fr = __node.SelectSingleNode("StartFrame");
                          if (__fr != null)
                              __i.StartFrame= int.Parse(__fr.InnerText);

                          __fr = __node.SelectSingleNode("EndFrame");
                          if (__fr != null)
                              __i.EndFrame= int.Parse(__fr.InnerText);

                          __fr = __node.SelectSingleNode("Frames");
                          if (__fr != null)
                              __i.Frames= __fr.InnerText;
                          
                          __i.Loop= bool.Parse(__node.SelectSingleNode("Loop").InnerText);
                      }
                      catch { }
                      creamXSceneData.AnimationData.Add( __i);
                  }
              }
        }
        
        public float floatParse(string flt)
        {
            return float.Parse(flt,CultureInfo.InvariantCulture);
        }
        
        private void LoadObjects(CreamXSceneData creamXSceneData, XmlDocument __doc)
        {
            try
            {
                XmlNode __el = __doc.SelectSingleNode("CreamXSceneData/ObjectData");
                foreach (XmlNode __node in __el.ChildNodes)
                {
                    CreamXObject __o = GetObject(__node);
                    XmlNode __dataNode = null;
                    string fileRef = "";
                    if (__node.Attributes.Count == 1 && __node.Attributes[0].Name == "fileref" &&
                        __node.Attributes[0].InnerText!=string.Empty)
                    {
                        __dataNode = GetDataNode(__node.Attributes[0].InnerText);
                        fileRef = __node.Attributes[0].InnerText;
                    }
                    else
                        __dataNode = __node;

                    if (__o is CreamXAnimatedObject)
                    {
                        CreamXAnimatedObject __an = (CreamXAnimatedObject)__o;
                        if (__dataNode.SelectSingleNode("AnimationInfo") != null &&
                            __dataNode.SelectSingleNode("AnimationInfo").Attributes["Ref"] != null)
                            __an.Animation = (AnimationInfo)creamXSceneData.GetAnimation(
                                __dataNode.SelectSingleNode("AnimationInfo").Attributes["Ref"].InnerText).Clone();
                    }
                    __o = LoadFromNode(creamXSceneData, ref __el, __dataNode, __o);
                    
                    __o.FileRef = fileRef;
                    if (__o.Template)
                        creamXSceneData.TemplateObjects.Add(__o);
                    else
                        creamXSceneData.Objects.Add(__o);
                }
            }
            catch (Exception err)
            {
                throw err;
            }
        }

        private XmlNode GetDataNode(string p)
        {
            string fname=_fileRoot+"\\"+ p;
            if (File.Exists(fname))
            {
                XmlDocument d = new XmlDocument();
                d.Load(_fileRoot + "\\" + p);

                return d.ChildNodes[0];
            }
            else
                return null;
        }

        private CreamXObject LoadFromNode(CreamXSceneData creamXSceneData, ref XmlNode __el, XmlNode __node, CreamXObject __o)
        {
            try
            {
                __o.Name = __node.SelectSingleNode("Name").InnerText;
                //Size
                __o.Scale = floatParse(__node.SelectSingleNode("Scale").InnerText);
                string __b = __node.SelectSingleNode("Template").InnerText;
                __o.Template = bool.Parse(__b);

                string col = __node.SelectSingleNode("Color").InnerText;

                string R= col.Substring(col.IndexOf("R:") + 2);
                R= R.Substring(0, R.IndexOf("G"));
                string G = col.Substring(col.IndexOf("G:") + 2);
                G = G.Substring(0, G.IndexOf("B"));
                string B = col.Substring(col.IndexOf("B:") + 2);
                B = B.Substring(0, B.IndexOf("A"));

                string A= col.Substring(col.IndexOf("A:") + 2);
                A = A.Substring(0, A.IndexOf("}"));
                __o.Color = new Color(byte.Parse(R),
                    byte.Parse(G),
                    byte.Parse(B),
                    byte.Parse(A));
                
                __o.Layer = int.Parse(__node.SelectSingleNode("Layer").InnerText);
                string __rotation = __node.SelectSingleNode("Rotation").InnerText;
                __o.Rotation = floatParse(__rotation);
                //Position
                string __x = __node.SelectSingleNode("Position/X").InnerText;
                string __y = __node.SelectSingleNode("Position/Y").InnerText;
                __o.SetPosition(new Microsoft.Xna.Framework.Vector2(floatParse(__x), floatParse(__y)));

                __x = __node.SelectSingleNode("Origin/X").InnerText;
                __y = __node.SelectSingleNode("Origin/Y").InnerText;
                __o.Origin = new Microsoft.Xna.Framework.Vector2(floatParse(__x), floatParse(__y));


                XmlNode __mat = __node.SelectSingleNode("Material");
                if (__mat != null && __mat.Attributes.Count > 0)
                    __o.Material = GetMaterial(creamXSceneData, __mat.Attributes["Ref"].Value);

                XmlNode __layer = __node.SelectSingleNode("Layer");
                if (__layer != null)
                {
                    __o.Layer = int.Parse(__layer.InnerText);
                }
                XmlNode objt = __node.SelectSingleNode("ObjectTypeAsVal");
                if (objt != null)
                    __o.SetObjectTypeFlag(uint.Parse(objt.InnerText));

                __el = __node.SelectSingleNode("ObjectType");
                if (__el != null)
                {
                    foreach (XmlNode __node1 in __el.ChildNodes)
                    {
                        //__o.ObjectType.Add(__node1.Attributes["name"].InnerText);
                    }
                }

                //Components
                __el = __node.SelectSingleNode("Components");
                if (__el != null)
                {
                    foreach (XmlNode __node1 in __el.ChildNodes)
                    {
                        object o = GetInstance(__node1.Name);
                        if (o != null)
                        {
                            foreach (PropertyInfo prop in o.GetType().GetProperties())
                            {
                                if (prop.GetCustomAttributes(true).Length > 0)
                                {
                                    Type t = prop.PropertyType.UnderlyingSystemType;
                                    XmlNode __valNode = __node1.SelectSingleNode(prop.Name);
                                    if (__valNode == null)
                                        continue;
                                    if (t == typeof(Vector2))
                                    {
                                        string val = __valNode.Attributes[0].InnerText;
                                        Vector2 __v = new Vector2(float.Parse(val.Substring(val.IndexOf("X:") + 2, val.LastIndexOf("Y") - (val.IndexOf("X:") + 2))),
                                            float.Parse(val.Substring(val.IndexOf("Y:") + 2, val.LastIndexOf("}") - (val.IndexOf("Y:") + 2))));
                                        prop.SetValue(o, __v, null);
                                    }
                                    else if (t == typeof(List<MountPoint>))
                                    {
                                        List<MountPoint> __lst = new List<MountPoint>();
                                        foreach (XmlNode __subnode in __valNode.ChildNodes)
                                        {
                                            MountPoint _mntpnt = new MountPoint();
                                            _mntpnt.Name = __subnode.SelectSingleNode("Name").InnerText;
                                            _mntpnt.Position = new Vector2(
                                                float.Parse(__subnode.SelectSingleNode("Position/X").InnerText),
                                                float.Parse(__subnode.SelectSingleNode("Position/Y").InnerText));
                                            _mntpnt.Rotation = float.Parse(__subnode.SelectSingleNode("Rotation").InnerText);
                                            // _mntpnt.objName= __subnode.SelectSingleNode("Object").Attributes["objectRef"].InnerText;
                                            __lst.Add(_mntpnt);
                                        }
                                        prop.SetValue(o, __lst, null);
                                    }
                                    else if (t == typeof(List<TileLayer>))
                                    {
                                        List<TileLayer> list = new List<TileLayer>();
                                        foreach (XmlNode __subnode in __valNode.ChildNodes)
                                        {
                                            TileLayer _mntpnt = new TileLayer();
                                            _mntpnt.Name = __subnode.Attributes["name"].InnerText;
                                            _mntpnt.Order=int.Parse(__subnode.Attributes["Order"].InnerText);
                                            _mntpnt.Visible= bool.Parse(__subnode.Attributes["Visible"].InnerText);
                                            _mntpnt.TileData = __subnode.InnerText.Replace('\r', ' ').Replace('\n', ' ').Replace(" ", "");
                                            
                                            list.Add(_mntpnt);
                                        }
                                        prop.SetValue(o, list, null);

                                    }
                                    else if (t == typeof(List<Mount>))
                                    {
                                        List<Mount> __lst = new List<Mount>();
                                        foreach (XmlNode __subnode in __valNode.ChildNodes)
                                        {
                                            Mount _mntpnt = new Mount();
                                            _mntpnt.ObjectRef = __subnode.SelectSingleNode("Name").Attributes["objectRef"].InnerText;
                                            _mntpnt.MountPoint = __o.GetComponent<CreamXMountPointComponent>().GetMountPoint(
                                                __subnode.SelectSingleNode("MountPoint").InnerText);
                                            _mntpnt.InheritVisibility = bool.Parse(__subnode.SelectSingleNode("InheritVisibility").InnerText);
                                            _mntpnt.SnapToMount = bool.Parse(__subnode.SelectSingleNode("SnapToMount").InnerText);
                                            // _mntpnt.objName= __subnode.SelectSingleNode("Object").Attributes["objectRef"].InnerText;
                                            __lst.Add(_mntpnt);
                                        }
                                        prop.SetValue(o, __lst, null);
                                    }
                                    else if (t == typeof(float))
                                        prop.SetValue(o, float.Parse(__valNode.Attributes[0].InnerText), null);
                                    else if (t == typeof(bool))
                                        prop.SetValue(o, bool.Parse(__valNode.Attributes[0].InnerText), null);
                                    else if (t == typeof(int))
                                        prop.SetValue(o, int.Parse(__valNode.Attributes[0].InnerText), null);
                                    else if (t.BaseType == typeof(Enum))
                                    {
                                        string str = __valNode.Attributes["value"].InnerText;
                                        object oo = Enum.Parse(t, str, true);
                                        prop.SetValue(o, oo, null);
                                    }
                                    else
                                        prop.SetValue(o, __valNode.Attributes[0].InnerText, null);


                                }
                            }
                            if (o is CreamXTileGridComponent)
                            {
                                __o = __o.ConvertToTileGrid();
                            }

                            __o.AddComponent(o as CreamXComponent);
                        }
                    }
                }
                __el = __node.SelectSingleNode("MountedObjects");
                if (__el != null)
                {
                    foreach (XmlNode __node1 in __el.ChildNodes)
                    {
                        Mount _mntpnt = new Mount();
                        XmlNode nddd = __node1.SelectSingleNode("Object");
                        _mntpnt.ObjectRef = __node1.SelectSingleNode("Object").Attributes["objectRef"].InnerText;
                        _mntpnt.MountPoint = __o.GetComponent<CreamXMountPointComponent>().GetMountPoint(
                            __node1.SelectSingleNode("MountPoint").InnerText);
                        _mntpnt.InheritVisibility =
                            bool.Parse(__node1.SelectSingleNode("InheritVisibility").InnerText);
                        // _mntpnt.objName= __subnode.SelectSingleNode("Object").Attributes["objectRef"].InnerText;
                        __o.MountedObjects.Add(_mntpnt);
                    }
                }
                if (__o is CreamXParticleEffect)
                {
                    __el = __node.SelectSingleNode("ParticleSettings");
                    if (__el != null)
                    {
                        CreamXParticleEffect pe = (CreamXParticleEffect)__o;
                        pe._emitter = new CreamX.Framework.Game.ParticleEffects.Emitter(5);
                        pe._emitter.MinInitialSpeed =float.Parse(__el.SelectSingleNode("minInitialSpeed").InnerText);
                        pe._emitter.MaxInitialSpeed = float.Parse(__el.SelectSingleNode("maxInitialSpeed").InnerText);
                        pe._emitter.MinAcceleration = float.Parse(__el.SelectSingleNode("minAcceleration").InnerText);
                        pe._emitter.MaxAcceleration = float.Parse(__el.SelectSingleNode("maxAcceleration").InnerText);
                        pe._emitter.MinLifetime = float.Parse(__el.SelectSingleNode("minLifetime").InnerText);
                        pe._emitter.MaxLifetime = float.Parse(__el.SelectSingleNode("maxLifetime").InnerText);
                        pe._emitter.MinScale = float.Parse(__el.SelectSingleNode("minScale").InnerText);
                        pe._emitter.MaxScale = float.Parse(__el.SelectSingleNode("maxScale").InnerText);
                        pe._emitter.MinNumParticles = int.Parse(__el.SelectSingleNode("minNumParticles").InnerText);
                        pe._emitter.MaxNumParticles = int.Parse(__el.SelectSingleNode("maxNumParticles").InnerText);
                        pe._emitter.MinRotationSpeed = float.Parse(__el.SelectSingleNode("minRotationSpeed").InnerText);
                        pe._emitter.MaxRotationSpeed = float.Parse(__el.SelectSingleNode("maxRotationSpeed").InnerText);
                        pe._emitter.spriteBlendMode = SpriteBlendMode.Additive;
                        pe._emitter.Owner= pe;
                        pe.SetupEmitter();
                    }
                }
                return __o;
            }
            catch (Exception err)
            {
                throw err;
            }
        }

        private CreamXObject GetObject(XmlNode __node)
        {
            if (__node.Name == "AnimatedObject")
                return new CreamXAnimatedObject();
            else if (__node.Name == "CreamXTileGrid")
                return new CreamXTileGrid();
            else if (__node.Name == "ParticleEffect")
                return new CreamXParticleEffect();
            else if (__node.Name == "Object")
                return new CreamXObject();
            else
                return null;
        }

        private void LoadTileMap(CreamXSceneData creamXSceneData, XmlDocument __doc)
        {
            //XmlNode __el = __doc.SelectSingleNode("CreamXSceneData/TileMap");
            //if (__el != null && __el.ChildNodes.Count>0)
            //{
            //    CreamXTileSet _tileset = new CreamXTileSet();
            //    //Get Tile Map size
            //    __el = __doc.SelectSingleNode("CreamXSceneData/TileMap");

            //    //Position
            //    _tileset.TilesX = int.Parse(__el.SelectSingleNode("Tiles/X").InnerText);
            //    _tileset.TilesY = int.Parse(__el.SelectSingleNode("Tiles/Y").InnerText);

            //    _tileset.TileCutSizeX = int.Parse(__el.SelectSingleNode("TileCutSize/X").InnerText);
            //    _tileset.TileCutSizeY = int.Parse(__el.SelectSingleNode("TileCutSize/Y").InnerText);
            //    int __x = int.Parse(__el.SelectSingleNode("TileDisplaySize/X").InnerText);
            //    int __y = int.Parse(__el.SelectSingleNode("TileDisplaySize/Y").InnerText);
            //    _tileset.TileDisplaySize = new Vector2(__x, __y);
            //    _tileset.SetTileData(__el.SelectSingleNode("TileData").InnerText);

            //    _tileset.Material = GetMaterial(creamXSceneData, __el.SelectSingleNode("Material").Attributes["Ref"].InnerText);

            //    creamXSceneData.TileSets.Add(_tileset);
            //}


            //__el = __doc.SelectSingleNode("CreamXSceneData/TileMapData");
            //if (__el != null)
            //{
            //    XmlNodeList __nodes = __el.SelectNodes("SpriteSheet");
            //    creamXSceneData.TileGridManager = new TileGridManager();

            //    foreach (XmlNode __spritesheet in __nodes)
            //    {
            //        CreamXMaterialBase __mat = GetMaterial(creamXSceneData, __spritesheet.SelectSingleNode("Material").Attributes["ref"].InnerText);
            //        SpriteSheet __sheet = new SpriteSheet(__mat);

            //        creamXSceneData.TileGridManager.SpriteSheets.Add(__spritesheet.Attributes["Name"].InnerText, __sheet);
                    
            //        //Load Source Sprites
            //        XmlNodeList __nodes1 = __spritesheet.SelectNodes("SourceSprite");
            //        foreach (XmlNode __source in __nodes1)
            //        {

            //            __sheet.AddSourceSprite(int.Parse(__source.SelectSingleNode("ID").InnerText),
            //                new Rectangle(int.Parse(__source.SelectSingleNode("Rect/X").InnerText),
            //                int.Parse(__source.SelectSingleNode("Rect/Y").InnerText),
            //                int.Parse(__source.SelectSingleNode("Rect/Width").InnerText),
            //                int.Parse(__source.SelectSingleNode("Rect/Height").InnerText)));
            //        }
            //    }
            //    __nodes = __el.SelectNodes("TileGrid");
                

            //    foreach (XmlNode __tilegrid in __nodes)
            //    {
            //        //CreamXMaterialBase __mat = GetMaterial(creamXSceneData, __el.SelectSingleNode("Material").Attributes["Ref"].InnerText);
            //        TileGrid __Grid= new TileGrid(int.Parse(__tilegrid.SelectSingleNode("TileWidth").InnerText),
            //            int.Parse(__tilegrid.SelectSingleNode("TileHeight").InnerText),
            //            int.Parse(__tilegrid.SelectSingleNode("NumOfTilesX").InnerText),
            //            int.Parse(__tilegrid.SelectSingleNode("NumOfTilesY").InnerText),
            //             new Vector2(int.Parse(__tilegrid.SelectSingleNode("OffsetX").InnerText),
            //            int.Parse(__tilegrid.SelectSingleNode("OffsetY").InnerText)),
            //            creamXSceneData.TileGridManager.SpriteSheets[__tilegrid.SelectSingleNode("SpriteSheet").Attributes["ref"].InnerText]);
            //        creamXSceneData.TileGridManager.TileGrids.Add(__Grid);


            //    }
            //}
        }

        private void LoadMaterials(CreamXSceneData creamXSceneData, XmlDocument __doc)
        {
            try
            {
                XmlNode __el = __doc.SelectSingleNode("CreamXSceneData/MaterialObjects");
                foreach (XmlNode __node in __el.ChildNodes)
                {
                    CreamXMaterialBase __mat = new CreamXMaterialBase();
                    __mat.Name = __node.Attributes["name"].Value;
                    
                    __mat.MaterialFilename = __node.SelectSingleNode("TextureFile").InnerText;
                    creamXSceneData.Materials.Add(__mat);
                }
            }
            catch (Exception err)
            {
                throw new ArgumentException("Error in load materials");
            }
        }

        private XmlNode LoadCamera(CreamXSceneData creamXSceneData, XmlDocument __doc)
        {
            XmlNode __el = __doc.SelectSingleNode("CreamXSceneData/Camera2D");
            string __x1 = __el.SelectSingleNode("Position/X").InnerText;
            string __y1 = __el.SelectSingleNode("Position/Y").InnerText;
            creamXSceneData.Camera.SetPosition(new Microsoft.Xna.Framework.Vector2(floatParse(__x1), floatParse(__y1)));
            __x1 = __el.SelectSingleNode("Size/X").InnerText;
            __y1 = __el.SelectSingleNode("Size/Y").InnerText;
            creamXSceneData.Camera.Size = new Microsoft.Xna.Framework.Vector2(floatParse(__x1), floatParse(__y1));
            creamXSceneData.SpecialSceneItems.Add(creamXSceneData.Camera);
            return __el;
        }

        private void LoadSceneSettings(XmlDocument __doc,CreamXSceneData data)
        {
            
            XmlNode __el = __doc.SelectSingleNode("CreamXSceneData/ContentRoot");
            data.ContentRoot = __el.Attributes["value"].InnerText;
        }
        
        private CreamXMaterialBase GetMaterial(CreamXSceneData data, string p)
        {
            foreach (CreamXMaterialBase __mat in data.Materials)
            {
                if (__mat.Name == p)
                    return __mat;
            }
            return null;
        }

    }
}
